# Copyright 2007 Eloff Enterprises
#
# Licensed under the BSD License (the "License").
# You may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#    http://rwig.googlecode.com/files/LICENSE
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
# implied.  See the License for the specific language governing
# permissions and limitations under the License.
#
# Originally developed by Daniel Eloff.

from __future__ import with_statement

import itertools
import string
import os

TEMPLATE_DIR = os.path.join(os.path.dirname(__file__), '../templates')

def generate_cpp(modules, output_dir):    
    templates = []
    for tpl in ['rwig.tpl', 'module_hpp.tpl', 'module_cpp.tpl', 'method_wrapper.tpl', 'init.tpl']:
        with file(os.path.join(TEMPLATE_DIR, tpl), 'r') as f:
            templates.append(string.Template(''.join(f.readlines()[:-1])))
        
    rwig_template, header_template, cpp_template, method_template, init_template = templates
    
    other_includes = []
    init_methods = []
    finalize_methods = []
    
    for module in modules:
        other_includes.append('#include "%s.h"' % module.alias)
        
        init_methods.append('\n\t'.join((
            'fail |= !%s_init();' % module.alias,
            'if(fail) goto finish;')))
        finalize_methods.append('%s_finalize();' % module.alias)
        
        method_wrappers = []
        other_decls = []
        cpp_init_methods = []
        cpp_final_methods = []
        
        if module.factory is None: # Module is the object
            get_obj_code = 'obj = module; module = NULL; /* Otherwise module/obj decrefed twice */ '
        else:
            get_obj_code = '\n\t'.join(('tmp = PyObject_GetAttrString(module, "%s");' % module.factory,
                                      'if(!tmp) goto finish;',
                                      '',
                                      'obj = PyObject_CallFunctionObjArgs(tmp, NULL);',
                                      'Py_DECREF(tmp);',
                                      'if(!obj) goto finish;'))
            
        bv_fromlist = 's'*len(module.fromlist)
        
        for method in module.methods:
            cpp_init_methods.append('\n\t'.join(('%s_f = PyObject_GetAttrString(obj, "%s");' % (method.alias, method.name),
                                                 'if(!%s_f) goto finish;' % method.alias)))
            
            cpp_final_methods.append('Py_XDECREF(%s_f);' % method.alias)
            
            arg_list = ', '.join('%s a%d' % (ty.c_type, i)
                                 for i, ty in enumerate(method.arg_types))
                       
            other_decls.append('%s %s(%s);' % (method.ret_type.c_type, method.alias, arg_list))
            build_args_str = ''.join(ty.bv_char for ty in method.arg_types)
            
            args = []
            for i, ty in enumerate(method.arg_types):
                if ty.conf_type == 'bytes':
                    args.append('a%d.buf, a%d.len' % (i, i))
                else:
                    args.append('a%d' % i)
            
            method_wrappers.append(method_template.substitute(
                name=method.alias,
                ret_type=method.ret_type.c_type,
                doc=method.doc,
                arg_build=build_args_str,
                arg_decl=arg_list,
                args=', '.join(args),
                convert=method.ret_type.convert_code % 'res',
                err_ret=method.ret_type.err_val))
        
        hpp_code = header_template.substitute(name=module.alias, other_decls='\n'.join(other_decls))
        
        cpp_code = cpp_template.substitute(
            name=module.alias,
            method_wrappers='\n\n'.join(method_wrappers),
            init=init_template.substitute(
                name=module.alias,
                bv_fromlist=bv_fromlist,
                fromlist='"' + '", "'.join(module.fromlist) + '"',
                module_name=module.name,
                get_object=get_obj_code,
                get_methods='\n\t\t'.join(cpp_init_methods)
            ),
            finalize='\n\t\t'.join(cpp_final_methods)
        )
            
        for code, suffix in [[hpp_code, '.h'], [cpp_code, '.c']]:
            with file(os.path.join(output_dir, module.alias + suffix), 'w') as f:
                print 'Creating', module.alias + suffix
                f.write(code)

    hpp_rwig = header_template.substitute(name='rwig', other_decls='\n'.join(other_includes))
    
    cpp_rwig = rwig_template.substitute(init_methods='\n\t\t'.join(init_methods),
                                         finalize_methods='\n\t\t'.join(finalize_methods))
        
    for code, suffix in [[hpp_rwig, '.h'], [cpp_rwig, '.c']]:
        with file(os.path.join(output_dir, 'rwig' + suffix), 'w') as f:
            print 'Creating rwig' + suffix
            f.write(code)
        